
#include <Inklude/ikFParallelInvoke.h>
#include <tbb/parallel_invoke.h>

static const size_t MAX_PARALLEL_INVOKABLE = 10;

void Inklude::ParallelInvoke(voidFunc& func0, voidFunc& func1)
{
    tbb::parallel_invoke(func0, func1);
}

void Inklude::ParallelInvoke(voidFunc& func0, voidFunc& func1, voidFunc& func2)
{
    tbb::parallel_invoke(func0, func1, func2);
}

void Inklude::ParallelInvoke(voidFunc& func0, voidFunc& func1, voidFunc& func2, voidFunc& func3)
{
    tbb::parallel_invoke(func0, func1, func2, func3);
}

void Inklude::ParallelInvoke(voidFunc& func0, voidFunc& func1, voidFunc& func2, voidFunc& func3, voidFunc& func4)
{
    tbb::parallel_invoke(func0, func1, func2, func3, func4);
}

void Inklude::ParallelInvoke(voidFunc& func0, voidFunc& func1, voidFunc& func2, voidFunc& func3, voidFunc& func4, voidFunc& func5)
{
    tbb::parallel_invoke(func0, func1, func2, func3, func4, func5);
}

void Inklude::ParallelInvoke(voidFunc& func0, voidFunc& func1, voidFunc& func2, voidFunc& func3, voidFunc& func4, voidFunc& func5, voidFunc& func6)
{
    tbb::parallel_invoke(func0, func1, func2, func3, func4, func5, func6);
}

void Inklude::ParallelInvoke(voidFunc& func0, voidFunc& func1, voidFunc& func2, voidFunc& func3, voidFunc& func4, voidFunc& func5, voidFunc& func6, voidFunc& func7)
{
    tbb::parallel_invoke(func0, func1, func2, func3, func4, func5, func6, func7);
}

void Inklude::ParallelInvoke(voidFunc& func0, voidFunc& func1, voidFunc& func2, voidFunc& func3, voidFunc& func4, voidFunc& func5, voidFunc& func6, voidFunc& func7, voidFunc& func8)
{
    tbb::parallel_invoke(func0, func1, func2, func3, func4, func5, func6, func7, func8);
}

void Inklude::ParallelInvoke(voidFunc& func0, voidFunc& func1, voidFunc& func2, voidFunc& func3, voidFunc& func4, voidFunc& func5, voidFunc& func6, voidFunc& func7, voidFunc& func8, voidFunc& func9)
{
    tbb::parallel_invoke(func0, func1, func2, func3, func4, func5, func6, func7, func8, func9);
}

void Inklude::ParallelInvoke(std::vector<voidFuncPtr>& funcVec)
{
    std::vector<voidFunc> func;
    for each (auto funcItem in funcVec)
    {
        func.push_back(*funcItem);
    }

    ParallelInvoke(func);
}

void Inklude::ParallelInvoke(std::vector<voidFunc>& funcVec)
{
    size_t vecSize = funcVec.size();
    size_t startIndex = 0;

    while(vecSize > MAX_PARALLEL_INVOKABLE)
    {
        __internal_parallel_invoke(funcVec, startIndex, MAX_PARALLEL_INVOKABLE);
        vecSize -= MAX_PARALLEL_INVOKABLE;
        startIndex += MAX_PARALLEL_INVOKABLE;
    }
    __internal_parallel_invoke(funcVec, startIndex, vecSize);
}

void Inklude::__internal_parallel_invoke(std::vector<voidFunc>& funcVec, size_t startIndex, size_t vecSize)
{
    switch(vecSize)
    {
    case 0:
        break;
    case 1:
        funcVec.at(startIndex)();
        break;
    case 2:
        Inklude::ParallelInvoke(funcVec.at(startIndex), 
                                funcVec.at(startIndex + 1));
        break;
    case 3:
        Inklude::ParallelInvoke(funcVec.at(startIndex), 
                                funcVec.at(startIndex + 1), 
                                funcVec.at(startIndex + 2));
        break;
    case 4:
        Inklude::ParallelInvoke(funcVec.at(startIndex), 
                                funcVec.at(startIndex + 1), 
                                funcVec.at(startIndex + 2),
                                funcVec.at(startIndex + 3));
        break;
    case 5:
        Inklude::ParallelInvoke(funcVec.at(startIndex), 
                                funcVec.at(startIndex + 1), 
                                funcVec.at(startIndex + 2),
                                funcVec.at(startIndex + 3), 
                                funcVec.at(startIndex + 4));
        break;
    case 6:
        Inklude::ParallelInvoke(funcVec.at(startIndex), 
                                funcVec.at(startIndex + 1), 
                                funcVec.at(startIndex + 2),
                                funcVec.at(startIndex + 3), 
                                funcVec.at(startIndex + 4), 
                                funcVec.at(startIndex + 5));
        break;
    case 7:
        Inklude::ParallelInvoke(funcVec.at(startIndex), 
                                funcVec.at(startIndex + 1), 
                                funcVec.at(startIndex + 2),
                                funcVec.at(startIndex + 3), 
                                funcVec.at(startIndex + 4), 
                                funcVec.at(startIndex + 5), 
                                funcVec.at(startIndex + 6));
        break;
    case 8:
        Inklude::ParallelInvoke(funcVec.at(startIndex), 
                                funcVec.at(startIndex + 1), 
                                funcVec.at(startIndex + 2),
                                funcVec.at(startIndex + 3), 
                                funcVec.at(startIndex + 4), 
                                funcVec.at(startIndex + 5), 
                                funcVec.at(startIndex + 6), 
                                funcVec.at(startIndex + 7));
        break;
    case 9:
        Inklude::ParallelInvoke(funcVec.at(startIndex), 
                                funcVec.at(startIndex + 1), 
                                funcVec.at(startIndex + 2),
                                funcVec.at(startIndex + 3), 
                                funcVec.at(startIndex + 4), 
                                funcVec.at(startIndex + 5), 
                                funcVec.at(startIndex + 6), 
                                funcVec.at(startIndex + 7), 
                                funcVec.at(startIndex + 8));
    case MAX_PARALLEL_INVOKABLE:
        Inklude::ParallelInvoke(funcVec.at(startIndex), 
                                funcVec.at(startIndex + 1), 
                                funcVec.at(startIndex + 2),
                                funcVec.at(startIndex + 3), 
                                funcVec.at(startIndex + 4), 
                                funcVec.at(startIndex + 5), 
                                funcVec.at(startIndex + 6), 
                                funcVec.at(startIndex + 7), 
                                funcVec.at(startIndex + 8), 
                                funcVec.at(startIndex + 9));
        break;
    default:
        break;
    }
}
